<?php
/**
 * 自定义的一个简单路由试验
 * 限定版本 php 7 及以上
 * 主要是因为用到 Throwable 这个7以上才有的
 */
//php版本限定7以上
namespace this;

use Exception;
use ReflectionMethod;
use Throwable;

$systemSet = [
    //是否启用详细提示
    'is_display_errors' => true
];
define('SYSTEM_SET',$systemSet);

$allowedNamespace = [
    'this',
    'controller',
    'model',
    'view'
];

$allowedRoutes = [
    'index' => '*',
    'test' => ['index'],
    'one' => '*'
];

load($allowedRoutes,$allowedNamespace);
function load($allowedRoutes,$allowedNamespace){
    // 解析 URL 路径
    $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    $urlArray = array_values(array_filter(explode('/', $path)));

    //校验
    if (count($urlArray) == 2){
        $namespace = $allowedNamespace[0];
        $className = $urlArray[0];
        $functionName = $urlArray[1];
    }elseif (count($urlArray) == 3){
        $namespace = $urlArray[0];
        $className = $urlArray[1];
        $functionName = $urlArray[2];
    }else{
        defaultPage();
        exit();
    }

    if (!in_array($namespace, $allowedNamespace)){
        echo '不允许访问';
        exit();
    }

    if (empty($allowedRoutes[$className])){
        echo '不允许访问';
        exit();
    }

    if($allowedRoutes[$className] != '*' && !in_array($functionName, $allowedRoutes[$className])) {
        echo '不允许访问';
        exit();
    }

    try {
        $fullClassName = $namespace.'\\'. $className;

        if ($namespace != 'this'){
            $loadDir = __DIR__.'\\'.$namespace.'\\'.$className.'.php';
            if (!@include_once $loadDir) {
                echo "加载文件失败: $loadDir";
                exit();
            }
        }


        if (class_exists($fullClassName) === false) {
            echo "{$fullClassName}类不存在";
            exit();
        }

        if (method_exists($fullClassName, $functionName) === false) {
            echo "{$fullClassName}类不存在{$functionName}";
            exit();
        }

        $class = new $fullClassName();
        $method = new ReflectionMethod($class, $functionName);

        // 方法可见性校验（即使白名单配置错误，也能二次防御）
        if (!$method->isPublic()) {
            throw new Exception("Method access denied");
        }

        $class->$functionName();
    }catch (Throwable $e){
        //Throwable 统一处理所有错误和异常,如:Fatal Error，Warning，Notice,Exception
        //Throwable 在php 7以前版本是没有的
        //Exception 仅能捕获显式抛出的异常（Exception）
        if (SYSTEM_SET['is_display_errors']){
            echo $e->getMessage();
        }else{
            echo 'url error';
        }
    }
}

function defaultPage() {
    echo '<br>-----------------你好啊-----------------<br>';
    echo (new DateTime)->format('YmdHisv').generateSecureRandom(6);
}

class index{
    public function main(){
        echo '你好 index 的 main';
    }

    private function tt(){
        echo '这是private tt';
    }
}

class test{
    function index(){
        echo '你好 test 的 index';
        echo '<br>';
        echo '<pre>';
        var_dump($_GET);
    }

    function hello(){
        echo '你好 test 的 hello';
    }

    private function aa(){
        echo '这是private aa';
    }
}

function generateSecureRandom($length = 16) {
    $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $max = 62; // strlen($charset)
    $maxValid = 256 - (256 % $max); // 消除取模偏差的关键值

    // 优先使用加密安全随机源
    if (function_exists('random_bytes')) { // PHP7+ 原生支持
        $bytes = random_bytes($length * 2);
    } elseif (function_exists('openssl_random_pseudo_bytes')) { // PHP5.3+ OpenSSL扩展
        $bytes = openssl_random_pseudo_bytes($length * 2);
    } else { // 最低兼容方案（非加密安全）
        $bytes = '';
        for ($i = 0; $i < $length; $i++) {
            $bytes .= chr(mt_rand(0, 255));
        }
    }

    $result = '';
    for ($i = 0; $i < $length; $i++) {
        // 消除取模偏差
        do {
            if (isset($bytes[$i])) {
                $byte = ord($bytes[$i]);
                $i++;
            } else {
                $byte = mt_rand(0, 255);
            }
        } while ($byte >= $maxValid);
        $result .= $charset[$byte % $max];
    }
    return substr($result, 0, $length);
}

